// Copyright (C) Calum Grant 2008

#include "cppscript"
#include "dynamic/extensions.hpp"
#include "tls.hpp"
#include "thread.hpp"

#include <cstdlib>
#include <memory>


namespace
{
	/// A thread-safe ref-counted object.
	/** When the reference count reaches zero, then the object is deleted.
		This is much like COM.
		\ingroup mem
		*/
	class ref_counted : cg::not_copyable, cg::not_assignable
	{
	public:
		/// Virtual destructor to call the destructor of the derived class.
		virtual ~ref_counted() { }

		/// Increments the reference count.
		void add_ref()
		{
			++m_count;
		}

		/// Decrements the reference count, and deletes the object if the ref count reaches 0.
		void release()
		{
			if( -- m_count == 0 ) delete this;
		}

	private:
		api::atomic_counter m_count;	 /// A thread-safe counter.
	};


	/// A reference to a ref-counted object.
	/** This object maintains a reference to a reference-counted object.
		It manages the reference count.
		\ingroup mem
		*/
	template<typename Ref>
	class ref_counted_impl
	{
	public:
		ref_counted_impl(Ref * r) : m_ref(r)
		{
			r->add_ref();
		}

		~ref_counted_impl()
		{
			m_ref->release();
		}

		ref_counted_impl(const ref_counted_impl & r) : m_ref(r.m_ref)
		{
			m_ref->add_ref();
		}

		ref_counted_impl & operator=(const ref_counted_impl & r)
		{
			r.m_ref->add_ref();
			m_ref->release();
			m_ref = r.m_ref();
		}

		/// Access the reference-counted variable.
		Ref * get() const { return m_ref; }

	private:
		Ref * m_ref;
	};
}

/// A mutually exclusive garbage-collected heap.
/** All objects in C++Script are managed by an apartment.
	Roughly speaking, there is one apartment per thread, and the programmer 
	can create further apartments as necessary.

	Apartments are "acquired" and "released", and an thread must enter an 
	apartment prior to accessing any data in the apartment.  

	This scheme is efficient because all objects are threadsafe, and there is
	no overhead when using objects or allocating memory.
	The only time there is overhead is when threads change
	apartments for some reason, for example to access an object in a different
	apartment.  Cross-apartment access must be strictly regulated using a 
	\ref dynamic::types::proxy_impl object.

	The apartment runs the garbage collector independently, which does not 
	block the operation of other threads.

	I realise this memory model is a bit wierd but it is completely scalable
	to any number of threads without overhead.

	\ingroup mem
*/
class dynamic::apartment : public ref_counted
{
	// Note order: the heap contains items managed by the gc, so it needs to be destroyed last.
	kingsley_heap m_heap;
	std::auto_ptr<gc::garbage_collector> m_gc;

public:
	apartment() : m_gc(gc::create_gc())
	{
	}

	/// Allocate an object in the apartment.
	void * malloc(std::size_t bytes)
	{
		return m_heap.malloc(bytes);
	}

	/// Free an object in the apartment.
	void free( void * p )
	{
		m_heap.free(p);
	}

	/// Defragment the heap (if supported by the memory manager).
	void defrag()
	{
		m_heap.defrag();
	}

	/// Access the apartment's garbage collector.
	gc::garbage_collector & gc() const 
	{ 
		return *m_gc; 
	}

	/// Gets a pointer to the current apartment.
	/** Note that there will be a different apartment per thread. */
	static apartment * current()
	{
		apartment * a = cg::tls<apartment*>::get();
		if(!cg::tls<apartment*>::get())
		{
			static internal::use_new_apartment static_apartment;
			a = cg::tls<apartment*>::get();

			// This is a little problematic in case multiple threads
			// call this.  static_apartment should not be used in multiple threads.
			// Normally this is fine since threads will create use_new_apartment themselves.
		}

		return a;
	}

	/// Enter the apartment.
	/** Releases the previous apartment, and wait for the new apartment to become free if necessary. */
	void enter()
	{
		current()->unlock();
		lock();
		cg::tls<apartment*>::set(this);
	}

private:
	api::mutex m_mutex;

public:
	/// Locks the apartment.
	/** To prevent deadlock, never lock more than one apartment at a time */
	void lock()
	{
		m_mutex.lock();
	}

	/// Unlocks the apartment.
	void unlock()
	{
		m_mutex.unlock();
	}
};


namespace
{
	/// A ref-counted \ref var which is used by a \ref proxy_impl to access a \ref var in another \ref apartment.
	/** The apartment in question gets cleaned up when all references to the apartment disappear.
			\ingroup mem */
	class proxy_stub_impl : public ref_counted
	{
	public:
		proxy_stub_impl(const var & v, apartment * from) : m_value(v), m_apartment(from)
		{ 
		}

		/// Retrieve the \ref apartment.
		apartment * apt() const 
		{ 
			return m_apartment.get(); 
		}

		/// The \ref var in the other apartment.
		var_impl & impl() const 
		{ 
			return m_value.impl(); 
		}

	private:
		var m_value;	/// The \ref var in the other \ref apartment.
		ref_counted_impl<apartment> m_apartment;	/// The \ref apartment that the var is in.
	};
}

namespace dynamic
{
namespace types
{
	/// Implements a proxy - which is a controlled way of accessing a \ref var in another \ref apartment.
	/** \ingroup mem \ingroup impl */
	class proxy_impl : public var_impl
	{
		class enter_apartment	// Memento pattern
		{
		public:
			enter_apartment(apartment * new_apartment) : m_previous(apartment::current())
			{
				new_apartment->enter();
			}

			~enter_apartment()
			{
				m_previous->enter();
			}

			apartment * from_apt() 
			{ 
				return m_previous; 
			}

		private:
			apartment * m_previous;
		};

		ref_counted_impl<proxy_stub_impl> m_stub;

	public:
		proxy_impl(proxy_stub_impl * stub) : m_stub(stub)
		{
		}

		var proxy()
		{
			return var(*this, var::assign_impl());
		}

		void copy_to(void * p) const
		{
			new(p) proxy_impl(*this);
		}

		apartment * to_apt() 
		{ 
			return m_stub.get()->apt(); 
		}

		var_impl & impl() 
		{ 
			return m_stub.get()->impl(); 
		}

		var_cmp_index comparison_index() 
		{ 
			return cmp_proxy; 
		}

		var_cmp_result compare2(const var & v)
		{
			proxy_impl * other = dynamic_cast<proxy_impl*>(&v.impl());

			if(other)
			{
				std::ptrdiff_t d = m_stub.get() - other->m_stub.get();
				return d<0 ? cmp_lt : d>0 ? cmp_gt: cmp_equal;
			}

			return cmp_not_equal;
		}

		var as_root()
		{ 
			return var(*this, var::assign_impl()); 
		}

		var as_nonroot() 
		{ 
			return var(*this, var::assign_impl()); 
		}

		var clone()
		{
			enter_apartment a(to_apt());

			try
			{
				return impl().clone().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		int as_int()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().as_int();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		bool as_bool()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().as_bool();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		double as_double()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().as_double();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		int max_args()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().max_args();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}


		void output(ostream & os)
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().output(os);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void output(wostream & os)
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().output(os);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}


		std::string class_name()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().class_name();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var call()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().call().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var member_call(const char *m)
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().member_call(m).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var call(const var & a0)
		{
			var a0_proxy = a0.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().call(a0_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var member_call(const char *m, const var & a0)
		{
			var a0_proxy = a0.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().member_call(m, a0_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var call(const var & a0, const var & a1)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().call(a0_proxy,a1_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var member_call(const char *m, const var & a0, const var & a1)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().member_call(m,a0_proxy,a1_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var call(const var & a0, const var & a1, const var & a2)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			var a2_proxy = a2.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().call(a0_proxy,a1_proxy,a2_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var member_call(const char *m, const var & a0, const var & a1, const var & a2)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			var a2_proxy = a2.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().member_call(m, a0_proxy,a1_proxy,a2_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var call(const var & a0, const var & a1, const var & a2, const var & a3)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			var a2_proxy = a2.proxy();
			var a3_proxy = a3.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().call(a0_proxy,a1_proxy,a2_proxy,a3_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var member_call(const char * m, const var & a0, const var & a1, const var & a2, const var & a3)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			var a2_proxy = a2.proxy();
			var a3_proxy = a3.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().member_call(m, a0_proxy,a1_proxy,a2_proxy,a3_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			var a2_proxy = a2.proxy();
			var a3_proxy = a3.proxy();
			var a4_proxy = a4.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().call(a0_proxy,a1_proxy,a2_proxy,a3_proxy,a4_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var member_call(const char * m, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			var a2_proxy = a2.proxy();
			var a3_proxy = a3.proxy();
			var a4_proxy = a4.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().member_call(m,a0_proxy,a1_proxy,a2_proxy,a3_proxy,a4_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			var a2_proxy = a2.proxy();
			var a3_proxy = a3.proxy();
			var a4_proxy = a4.proxy();
			var a5_proxy = a5.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().call(a0_proxy,a1_proxy,a2_proxy,a3_proxy,a4_proxy,a5_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var member_call(const char * m, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			var a2_proxy = a2.proxy();
			var a3_proxy = a3.proxy();
			var a4_proxy = a4.proxy();
			var a5_proxy = a5.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().member_call(m,a0_proxy,a1_proxy,a2_proxy,a3_proxy,a4_proxy,a5_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			var a2_proxy = a2.proxy();
			var a3_proxy = a3.proxy();
			var a4_proxy = a4.proxy();
			var a5_proxy = a5.proxy();
			var a6_proxy = a6.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().call(a0_proxy,a1_proxy,a2_proxy,a3_proxy,a4_proxy,a5_proxy,a6_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var member_call(const char * m, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			var a2_proxy = a2.proxy();
			var a3_proxy = a3.proxy();
			var a4_proxy = a4.proxy();
			var a5_proxy = a5.proxy();
			var a6_proxy = a6.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().member_call(m,a0_proxy,a1_proxy,a2_proxy,a3_proxy,a4_proxy,a5_proxy,a6_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			var a2_proxy = a2.proxy();
			var a3_proxy = a3.proxy();
			var a4_proxy = a4.proxy();
			var a5_proxy = a5.proxy();
			var a6_proxy = a6.proxy();
			var a7_proxy = a7.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().call(a0_proxy,a1_proxy,a2_proxy,a3_proxy,a4_proxy,a5_proxy,a6_proxy,a7_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var member_call(const char * m, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			var a2_proxy = a2.proxy();
			var a3_proxy = a3.proxy();
			var a4_proxy = a4.proxy();
			var a5_proxy = a5.proxy();
			var a6_proxy = a6.proxy();
			var a7_proxy = a7.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().member_call(m,a0_proxy,a1_proxy,a2_proxy,a3_proxy,a4_proxy,a5_proxy,a6_proxy,a7_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			var a2_proxy = a2.proxy();
			var a3_proxy = a3.proxy();
			var a4_proxy = a4.proxy();
			var a5_proxy = a5.proxy();
			var a6_proxy = a6.proxy();
			var a7_proxy = a7.proxy();
			var a8_proxy = a8.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().call(a0_proxy,a1_proxy,a2_proxy,a3_proxy,a4_proxy,a5_proxy,a6_proxy,a7_proxy,a8_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var member_call(const char * m, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			var a2_proxy = a2.proxy();
			var a3_proxy = a3.proxy();
			var a4_proxy = a4.proxy();
			var a5_proxy = a5.proxy();
			var a6_proxy = a6.proxy();
			var a7_proxy = a7.proxy();
			var a8_proxy = a8.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().member_call(m, a0_proxy,a1_proxy,a2_proxy,a3_proxy,a4_proxy,a5_proxy,a6_proxy,a7_proxy,a8_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8, const var & a9)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			var a2_proxy = a2.proxy();
			var a3_proxy = a3.proxy();
			var a4_proxy = a4.proxy();
			var a5_proxy = a5.proxy();
			var a6_proxy = a6.proxy();
			var a7_proxy = a7.proxy();
			var a8_proxy = a8.proxy();
			var a9_proxy = a9.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().call(a0_proxy,a1_proxy,a2_proxy,a3_proxy,a4_proxy,a5_proxy,a6_proxy,a7_proxy,a8_proxy,a9_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var member_call(const char * m, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8, const var & a9)
		{
			var a0_proxy = a0.proxy();
			var a1_proxy = a1.proxy();
			var a2_proxy = a2.proxy();
			var a3_proxy = a3.proxy();
			var a4_proxy = a4.proxy();
			var a5_proxy = a5.proxy();
			var a6_proxy = a6.proxy();
			var a7_proxy = a7.proxy();
			var a8_proxy = a8.proxy();
			var a9_proxy = a9.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().member_call(m,a0_proxy,a1_proxy,a2_proxy,a3_proxy,a4_proxy,a5_proxy,a6_proxy,a7_proxy,a8_proxy,a9_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		int size()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().size();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		std::size_t capacity()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().capacity();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void get_range(std::size_t size, std::pair<std::size_t, std::size_t> & out)
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().get_range(size, out);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void resize(std::size_t n)
		{
			enter_apartment a(to_apt());
			try
			{
				impl().resize(n);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		bool empty()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().empty();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void clear()
		{
			enter_apartment a(to_apt());
			try
			{
				impl().clear();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var front()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().front().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var back()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().back().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var key()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().key().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var value()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().value().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var deref()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().deref().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var begin()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().begin().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var end()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().end().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var rbegin()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().rbegin().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var rend()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().rend().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void op_inc()
		{
			enter_apartment a(to_apt());
			try
			{
				impl().op_inc();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void op_dec()
		{
			enter_apartment a(to_apt());
			try
			{
				impl().op_dec();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var enumerator()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().enumerator().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var reverse_enumerator()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().reverse_enumerator().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var keys()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().keys().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var values()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().values().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var get_member(const var & i)
		{
			var i_proxy = i.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().get_member(i_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var get_member(int i)
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().get_member(i).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var get_member(const char* i)
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().get_member(i).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void set_member(const var & a, const var & b)
		{
			var a_proxy = a.proxy();
			var b_proxy = b.proxy();
			enter_apartment ea(to_apt());
			try
			{
				impl().set_member(a_proxy,b_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void set_member(int a, const var & b)
		{
			var b_proxy = b.proxy();
			enter_apartment ea(to_apt());
			try
			{
				impl().set_member(a,b_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void set_member(const char *a, const var & b)
		{
			var b_proxy = b.proxy();
			enter_apartment ea(to_apt());
			try
			{
				impl().set_member(a,b_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void push_back(const var & v)
		{
			var v_proxy = v.proxy();
			enter_apartment ea(to_apt());
			try
			{
				impl().push_back(v_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void push_front(const var & v)
		{
			var v_proxy = v.proxy();
			enter_apartment ea(to_apt());
			try
			{
				impl().push_front(v_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var pop_back()
		{
			enter_apartment ea(to_apt());
			try
			{
				return impl().pop_back().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var pop_front()
		{
			enter_apartment ea(to_apt());
			try
			{
				return impl().pop_front().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		bool contains(const var & s)
		{
			var s_proxy = s.proxy();
			enter_apartment ea(to_apt());
			try
			{
				return impl().contains(s_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var op_add(const var & v)
		{
			var v_proxy = v.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().op_add(v_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var op_sub(const var & v)
		{
			var v_proxy = v.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().op_sub(v_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var op_mul(const var & v)
		{
			var v_proxy = v.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().op_mul(v_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var op_div(const var & v)
		{
			var v_proxy = v.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().op_div(v_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var op_mod(const var & v)
		{
			var v_proxy = v.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().op_mod(v_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var op_pos()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().op_pos().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var op_neg()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().op_neg().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var op_inv()
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().op_inv().proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var op_lshift(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().op_lshift(n_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var op_rshift(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().op_rshift(n_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var op_and(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().op_and(n_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var op_or(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().op_or(n_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var op_xor(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().op_xor(n_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void assign_add(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				impl().assign_add(n_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void assign_sub(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				impl().assign_sub(n_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void assign_mul(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				impl().assign_mul(n_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void assign_div(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				impl().assign_div(n_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void assign_mod(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				impl().assign_mod(n_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void assign_lshift(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				impl().assign_lshift(n_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void assign_rshift(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				impl().assign_rshift(n_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void assign_and(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				impl().assign_and(n_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void assign_or(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				impl().assign_or(n_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void assign_xor(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				impl().assign_xor(n_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void erase(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				impl().erase(n_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void insert(const var & n)
		{
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				impl().insert(n_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		void insert(const var & m, const var & n)
		{
			var m_proxy = m.proxy();
			var n_proxy = n.proxy();
			enter_apartment a(to_apt());
			try
			{
				impl().insert(m_proxy,n_proxy);
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var member_inc(const char * n)
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().member_inc(n).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var member_dec(const char * n)
		{
			enter_apartment a(to_apt());
			try
			{
				return impl().member_dec(n).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var member_add(const char * n, const var & m)
		{
			var m_proxy = m.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().member_add(n,m_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}

		var member_sub(const char * n, const var & m)
		{
			var m_proxy = m.proxy();
			enter_apartment a(to_apt());
			try
			{
				return impl().member_sub(n,m_proxy).proxy();
			}
			catch(var & ex)
			{
				throw ex.proxy();
			}
		}
	};
}
}


var dynamic::internal::proxy(const var & v)
{
	return proxy(v, apartment::current());
}


var dynamic::internal::proxy(const var & v, apartment * from)
{
	return var(types::proxy_impl(new proxy_stub_impl(v, from)), var::assign_impl());
}


// This disables the custom allocator
#define DEBUG_MALLOC 1


void * dynamic::internal::apartment_malloc(std::size_t bytes)
{
#if DEBUG_MALLOC
	return ::malloc(bytes);
#else
	return apartment::current()->malloc( bytes );
#endif
}


void dynamic::internal::apartment_free(void *p)
{
#if DEBUG_MALLOC
	::free(p);
#else
	apartment::current()->free(p);
#endif
}


void dynamic::internal::apartment_defrag()
{
	apartment::current()->defrag();
}


internal::leave_apartment::leave_apartment()
{
	apartment::current()->unlock();
}


internal::leave_apartment::~leave_apartment()
{
	apartment::current()->lock();
}


var dynamic::shared(const var & cmd)
{
	return internal::proxy(cmd, new apartment())();
}


var dynamic::global(const var & cmd)
{
	static apartment * appt = new apartment();
	static var p = proxy(var(), appt);
	return proxy(cmd, appt)();
}


internal::use_new_apartment::use_new_apartment() : m_previous(cg::tls<apartment*>::get())
{
	if(m_previous) m_previous->unlock();
	apartment * a = new apartment();
	a->add_ref();
	a->lock();
	cg::tls<apartment*>::set( a );
}


internal::use_new_apartment::~use_new_apartment()
{
	apartment * current = cg::tls<apartment*>::get();
	current->unlock();
	current->release();
	if(m_previous) m_previous->lock();
	cg::tls<apartment*>::set(m_previous);
}


dynamic::gc::garbage_collector & dynamic::gc::global_gc()
{
	return apartment::current()->gc();
}
